export const attributeMap = new Map([
  ['style', 'style'],
  ['width', 'width'],
  ['height', 'height'],
  ['margin', 'margin'],
  ['marginLeft', 'marginLeft'],
  ['marginRight', 'marginRight'],
  ['marginTop', 'marginTop'],
  ['marginBottom', 'marginBottom'],
  ['align', 'align'],
  ['aspectRatio', 'aspectRatio'],
  ['axis', 'axis'],
  ['inset', 'inset'],
  ['grid', 'grid'],
  ['label', 'label'],
  ['padding', 'padding'],
  ['xScale', 'x.type'],
  ['xDomain', 'x.domain'],
  ['xRange', 'x.range'],
  ['xNice', 'x.nice'],
  ['xInset', 'x.inset'],
  ['xInsetLeft', 'x.insetLeft'],
  ['xInsetRight', 'x.insetRight'],
  ['xClamp', 'x.clamp'],
  ['xRound', 'x.round'],
  ['xAlign', 'x.align'],
  ['xPadding', 'x.padding'],
  ['xPaddingInner', 'x.paddingInner'],
  ['xPaddingOuter', 'x.paddingOuter'],
  ['xAxis', 'x.axis'],
  ['xTicks', 'x.ticks'],
  ['xTickSize', 'x.tickSize'],
  ['xTickSpacing', 'x.tickSpacing'],
  ['xTickPadding', 'x.tickPadding'],
  ['xTickFormat', 'x.tickFormat'],
  ['xTickRotate', 'x.tickRotate'],
  ['xGrid', 'x.grid'],
  ['xLine', 'x.line'],
  ['xLabel', 'x.label'],
  ['xLabelAnchor', 'x.labelAnchor'],
  ['xLabelArrow', 'x.labelArrow'],
  ['xLabelOffset', 'x.labelOffset'],
  ['xFontVariant', 'x.fontVariant'],
  ['xAriaLabel', 'x.ariaLabel'],
  ['xAriaDescription', 'x.ariaDescription'],
  ['xPercent', 'x.percent'],
  ['xReverse', 'x.reverse'],
  ['xZero', 'x.zero'],
  ['xBase', 'x.base'],
  ['xExponent', 'x.exponent'],
  ['xConstant', 'x.constant'],
  ['yScale', 'y.type'],
  ['yDomain', 'y.domain'],
  ['yRange', 'y.range'],
  ['yNice', 'y.nice'],
  ['yInset', 'y.inset'],
  ['yInsetTop', 'y.insetTop'],
  ['yInsetBottom', 'y.insetBottom'],
  ['yClamp', 'y.clamp'],
  ['yRound', 'y.round'],
  ['yAlign', 'y.align'],
  ['yPadding', 'y.padding'],
  ['yPaddingInner', 'y.paddingInner'],
  ['yPaddingOuter', 'y.paddingOuter'],
  ['yAxis', 'y.axis'],
  ['yTicks', 'y.ticks'],
  ['yTickSize', 'y.tickSize'],
  ['yTickSpacing', 'y.tickSpacing'],
  ['yTickPadding', 'y.tickPadding'],
  ['yTickFormat', 'y.tickFormat'],
  ['yTickRotate', 'y.tickRotate'],
  ['yGrid', 'y.grid'],
  ['yLine', 'y.line'],
  ['yLabel', 'y.label'],
  ['yLabelAnchor', 'y.labelAnchor'],
  ['yLabelArrow', 'y.labelArrow'],
  ['yLabelOffset', 'y.labelOffset'],
  ['yFontVariant', 'y.fontVariant'],
  ['yAriaLabel', 'y.ariaLabel'],
  ['yAriaDescription', 'y.ariaDescription'],
  ['yPercent', 'y.percent'],
  ['yReverse', 'y.reverse'],
  ['yZero', 'y.zero'],
  ['yBase', 'y.base'],
  ['yExponent', 'y.exponent'],
  ['yConstant', 'y.constant'],
  ['facetMargin', 'facet.margin'],
  ['facetMarginTop', 'facet.marginTop'],
  ['facetMarginBottom', 'facet.marginBottom'],
  ['facetMarginLeft', 'facet.marginLeft'],
  ['facetMarginRight', 'facet.marginRight'],
  ['facetGrid', 'facet.grid'],
  ['facetLabel', 'facet.label'],
  ['fxDomain', 'fx.domain'],
  ['fxRange', 'fx.range'],
  ['fxInset', 'fx.inset'],
  ['fxInsetLeft', 'fx.insetLeft'],
  ['fxInsetRight', 'fx.insetRight'],
  ['fxRound', 'fx.round'],
  ['fxAlign', 'fx.align'],
  ['fxPadding', 'fx.padding'],
  ['fxPaddingInner', 'fx.paddingInner'],
  ['fxPaddingOuter', 'fx.paddingOuter'],
  ['fxAxis', 'fx.axis'],
  ['fxTicks', 'fx.ticks'],
  ['fxTickSize', 'fx.tickSize'],
  ['fxTickSpacing', 'fx.tickSpacing'],
  ['fxTickPadding', 'fx.tickPadding'],
  ['fxTickFormat', 'fx.tickFormat'],
  ['fxTickRotate', 'fx.tickRotate'],
  ['fxGrid', 'fx.grid'],
  ['fxLine', 'fx.line'],
  ['fxLabel', 'fx.label'],
  ['fxLabelAnchor', 'fx.labelAnchor'],
  ['fxLabelOffset', 'fx.labelOffset'],
  ['fxFontVariant', 'fx.fontVariant'],
  ['fxAriaLabel', 'fx.ariaLabel'],
  ['fxAriaDescription', 'fx.ariaDescription'],
  ['fxReverse', 'fx.reverse'],
  ['fyDomain', 'fy.domain'],
  ['fyRange', 'fy.range'],
  ['fyInset', 'fy,inset'],
  ['fyInsetTop', 'fy.insetTop'],
  ['fyInsetBottom', 'fy.insetBottom'],
  ['fyRound', 'fy.round'],
  ['fyAlign', 'fy.align'],
  ['fyPadding', 'fy.padding'],
  ['fyPaddingInner', 'fy.paddingInner'],
  ['fyPaddingOuter', 'fy.paddingOuter'],
  ['fyAxis', 'fy.axis'],
  ['fyTicks', 'fy.ticks'],
  ['fyTickSize', 'fy.tickSize'],
  ['fyTickSpacing', 'fy.tickSpacing'],
  ['fyTickPadding', 'fy.tickPadding'],
  ['fyTickFormat', 'fy.tickFormat'],
  ['fyTickRotate', 'fy.tickRotate'],
  ['fyGrid', 'fy.grid'],
  ['fyLine', 'fy.line'],
  ['fyLabel', 'fy.label'],
  ['fyLabelAnchor', 'fy.labelAnchor'],
  ['fyLabelOffset', 'fy.labelOffset'],
  ['fyFontVariant', 'fy.fontVariant'],
  ['fyAriaLabel', 'fy.ariaLabel'],
  ['fyAriaDescription', 'fy.ariaDescription'],
  ['fyReverse', 'fy.reverse'],
  ['colorScale', 'color.type'],
  ['colorDomain', 'color.domain'],
  ['colorRange', 'color.range'],
  ['colorClamp', 'color.clamp'],
  ['colorN', 'color.n'],
  ['colorNice', 'color.nice'],
  ['colorScheme', 'color.scheme'],
  ['colorInterpolate', 'color.interpolate'],
  ['colorPivot', 'color.pivot'],
  ['colorSymmetric', 'color.symmetric'],
  ['colorLabel', 'color.label'],
  ['colorPercent', 'color.percent'],
  ['colorReverse', 'color.reverse'],
  ['colorZero', 'color.zero'],
  ['colorTickFormat', 'color.tickFormat'],
  ['colorBase', 'color.base'],
  ['colorExponent', 'color.exponent'],
  ['colorConstant', 'color.constant'],
  ['opacityScale', 'opacity.type'],
  ['opacityDomain', 'opacity.domain'],
  ['opacityRange', 'opacity.range'],
  ['opacityClamp', 'opacity.clamp'],
  ['opacityNice', 'opacity.nice'],
  ['opacityLabel', 'opacity.label'],
  ['opacityPercent', 'opacity.percent'],
  ['opacityReverse', 'opacity.reverse'],
  ['opacityZero', 'opacity.zero'],
  ['opacityTickFormat', 'opacity.tickFormat'],
  ['opacityBase', 'opacity.base'],
  ['opacityExponent', 'opacity.exponent'],
  ['opacityConstant', 'opacity.constant'],
  ['symbolScale', 'symbol.type'],
  ['symbolDomain', 'symbol.domain'],
  ['symbolRange', 'symbol.range'],
  ['rScale', 'r.type'],
  ['rDomain', 'r.domain'],
  ['rRange', 'r.range'],
  ['rClamp', 'r.clamp'],
  ['rNice', 'r.nice'],
  ['rLabel', 'r.label'],
  ['rPercent', 'r.percent'],
  ['rZero', 'r.zero'],
  ['rBase', 'r.base'],
  ['rExponent', 'r.exponent'],
  ['rConstant', 'r.constant'],
  ['lengthScale', 'length.type'],
  ['lengthDomain', 'length.domain'],
  ['lengthRange', 'length.range'],
  ['lengthClamp', 'length.clamp'],
  ['lengthNice', 'length.nice'],
  ['lengthPercent', 'length.percent'],
  ['lengthZero', 'length.zero'],
  ['lengthBase', 'length.base'],
  ['lengthExponent', 'length.exponent'],
  ['lengthConstant', 'length.constant'],
  ['projectionType', 'projection.type'],
  ['projectionParallels', 'projection.parallels'],
  ['projectionPrecision', 'projection.precision'],
  ['projectionRotate', 'projection.rotate'],
  ['projectionDomain', 'projection.domain'],
  ['projectionInset', 'projection.inset'],
  ['projectionInsetLeft', 'projection.insetLeft'],
  ['projectionInsetRight', 'projection.insetRight'],
  ['projectionInsetTop', 'projection.insetTop'],
  ['projectionInsetBottom', 'projection.insetBottom'],
  ['projectionClip', 'projection.clip']
]);

function setProperty(object, path, value) {
  for (let i = 0; i < path.length; ++i) {
    const key = path[i];
    if (i === path.length - 1) {
      object[key] = value;
    } else {
      object = (object[key] || (object[key] = {}));
    }
  }
}

export function setAttributes(attributes, spec, symbols) {
  // populate top-level and scale properties
  for (const key in attributes) {
    const specKey = attributeMap.get(key);
    if (specKey == null) {
      throw new Error(`Unrecognized plot attribute: ${key}`);
    }
    const value = attributes[key];
    if (typeof value === 'symbol') {
      symbols.push(key);
    } else if (value !== undefined) {
      setProperty(spec, specKey.split('.'), value);
    }
  }
}
